/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package info.niwota.dc;

import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

/**
 * Manages each alarm
 */
public class SetAlarm extends PreferenceActivity implements
		Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener {

	// private EditTextPreference mLabel;

	private String mLabel;
	//
	private CheckBoxPreference mAlarmOnPref;
	private Preference mTimePref;
	private AlarmPreference mAlarmPref;
	//private CheckBoxPreference mVibratePref;
	// private RepeatPreference mRepeatPref;
	private ContentObserver mAlarmsChangeObserver;
	// private MenuItem mDeleteAlarmItem;
	// private MenuItem mTestAlarmItem;

	private int mId;
	
	private int mHour;
	private int mMinutes;
	
	private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();

	private boolean mReportAlarmCalled;

	private long mEventDateTime;

	private static final int DIALOG_TIMEPICKER = 0;

	private class RingtoneChangedListener implements
			AlarmPreference.IRingtoneChangedListener {
		public void onRingtoneChanged(Uri ringtoneUri) {
			saveAlarm(false);
		}
	}

	// private class OnRepeatChangedObserver implements
	// RepeatPreference.OnRepeatChangedObserver {
	// public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek) {
	// if (!mDaysOfWeek.equals(daysOfWeek)) {
	// mDaysOfWeek.set(daysOfWeek);
	// saveAlarm(true);
	// }
	// }
	// public Alarms.DaysOfWeek getDaysOfWeek() {
	// return mDaysOfWeek;
	// }
	// }

	private class AlarmsChangeObserver extends ContentObserver {
		public AlarmsChangeObserver() {
			super(new Handler());
		}

		@Override
		public void onChange(boolean selfChange) {
			Alarms.getAlarm(getContentResolver(), SetAlarm.this, mId);
		}
	}

	/**
	 * Set an alarm. Requires an Alarms.ID to be passed in as an extra
	 */
	@Override
	protected void onCreate(Bundle icicle) {
		requestWindowFeature(Window.FEATURE_LEFT_ICON);
		
		super.onCreate(icicle);
		
		addPreferencesFromResource(R.xml.alarm_prefs);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
				R.drawable.icon);
		//
		// mLabel = (EditTextPreference) findPreference("label");
		// mLabel.setOnPreferenceChangeListener(
		// new Preference.OnPreferenceChangeListener() {
		// public boolean onPreferenceChange(Preference p,
		// Object newValue) {
		// p.setSummary((String) newValue);
		// saveAlarm(false, (String) newValue);
		// return true;
		// }
		// });

		mAlarmOnPref = (CheckBoxPreference) findPreference("on");
		mTimePref = findPreference("time");
		mAlarmPref = (AlarmPreference) findPreference("alarm");
		//mVibratePref = (CheckBoxPreference) findPreference("vibrate");
		// mRepeatPref = (RepeatPreference) findPreference("setRepeat");

		Intent i = getIntent();
		mId = i.getIntExtra(Alarms.ID, -1);
		if (Log.LOGV) {
			Log.v("SetAlarm, alarm id: " + mId);
		}
		mReportAlarmCalled = false;
		/* load alarm details from database */
		Alarms.getAlarm(getContentResolver(), this, mId);
		/*
		 * This should never happen, but does occasionally with the monkey. I
		 * believe it's a race condition where a deleted alarm is opened before
		 * the alarm list is refreshed.
		 */
		if (!mReportAlarmCalled) {
			Log.e("reportAlarm never called!");
			finish();
		}

		mAlarmsChangeObserver = new AlarmsChangeObserver();
		getContentResolver().registerContentObserver(
				Alarms.AlarmColumns.CONTENT_URI, true, mAlarmsChangeObserver);

		mAlarmPref.setRingtoneChangedListener(new RingtoneChangedListener());
		// mRepeatPref.setOnRepeatChangedObserver(new
		// OnRepeatChangedObserver());
		//
		mEventDateTime = i.getLongExtra(Alarms.EVENT_TIME, System.currentTimeMillis());
		if (Log.LOGV) {
			Log.v("SetAlarm, mEventDateTime: " + mEventDateTime);
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		getContentResolver().unregisterContentObserver(mAlarmsChangeObserver);
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		TimePickerDialog d = null;
		switch (id) {
		case DIALOG_TIMEPICKER:
			d = new TimePickerDialog(SetAlarm.this, this, 0, 0, mEventDateTime);
			d.setTitle(getResources().getString(R.string.time));
			d.setCustomTitle(null);
			break;
		default:
		}
		return d;
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);

		switch (id) {
		case DIALOG_TIMEPICKER:
			TimePickerDialog d = (TimePickerDialog) dialog;
			d.updateTime(mHour, mMinutes);
			break;
		}
	}

	@Override
	public boolean onPreferenceTreeClick(PreferenceScreen prefScreen,
			Preference pref) {
		if (pref == mTimePref) {
			showDialog(DIALOG_TIMEPICKER);
		} else if (pref == mAlarmOnPref) {
			saveAlarm(true);
		}
		
//		else if (pref == mVibratePref) {
//			saveAlarm(false);
//		}

		return super.onPreferenceTreeClick(prefScreen, pref);
	}

	/**
	 */
	public void onTimeSet(HourMinutePicker view, int hourOfDay, int minute) {
		mHour = hourOfDay;
		mMinutes = minute;
		mAlarmOnPref.setChecked(true);
		saveAlarm(true);
	}

	/**
	 * Load alarm settings
	 * 
	 * Alarms.AlarmSettings implementation. Database feeds current settings in
	 * through this call
	 */
	public void reportAlarm(int idx, boolean enabled, int hour, int minutes,
			Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label,
			String alert) {

		if (label == null || label.length() == 0) {
			label = ""; // getString(R.string.default_label);
		}
		//
		mLabel = label;
		setTitle(label);
		// mLabel.setText(label);
		// mLabel.setSummary(label);
		//
		mHour = hour;
		mMinutes = minutes;
		mAlarmOnPref.setChecked(enabled);
		mDaysOfWeek.set(daysOfWeek);
		
		//mVibratePref.setChecked(vibrate);

		if (alert == null || alert.length() == 0) {
			if (Log.LOGV)
				Log.v("****** reportAlarm null or 0-length alert");
			mAlarmPref.mAlert = getDefaultAlarm();
			if (mAlarmPref.mAlert == null) {
				Log.e("****** Default Alarm null");
			}
		} else {
			mAlarmPref.mAlert = Uri.parse(alert);
			if (mAlarmPref.mAlert == null) {
				Log.e("****** Parsed null alarm. URI: " + alert);
			}
		}
		if (Log.LOGV)
			Log.v("****** reportAlarm uri " + alert + " alert "
					+ mAlarmPref.mAlert);
		updateTime();
		// updateRepeat();
		updateAlarm(mAlarmPref.mAlert);

		mReportAlarmCalled = true;
	}

	/**
	 * picks the first alarm available
	 */
	private Uri getDefaultAlarm() {
		RingtoneManager ringtoneManager = new RingtoneManager(this);
		ringtoneManager.setType(RingtoneManager.TYPE_ALARM);
		return ringtoneManager.getRingtoneUri(0);
	}

	private void updateTime() {
		if (Log.LOGV)
			Log.v("updateTime " + mId);
		mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes,
				mDaysOfWeek));
	}

	private void updateAlarm(Uri ringtoneUri) {
		if (Log.LOGV)
			Log.v("updateAlarm " + mId);
		Ringtone ringtone = RingtoneManager.getRingtone(SetAlarm.this,
				ringtoneUri);
		if (ringtone != null) {
			mAlarmPref.setSummary(ringtone.getTitle(SetAlarm.this));
		}
	}

	// private void updateRepeat() {
	// if (Log.LOGV) Log.v("updateRepeat " + mId);
	// //mRepeatPref.setSummary(mDaysOfWeek.toString(this, true));
	// }

	private void saveAlarm(boolean popToast) {
		saveAlarm(popToast, mLabel); // mLabel.getText()
	}

	/**
	 * This version of saveAlarm uses the passed in label since mLabel may
	 * contain the old value (i.e. during the preference value change).
	 */
	private void saveAlarm(boolean popToast, String label) {
		if (mReportAlarmCalled && mAlarmPref.mAlert != null) {
			String alertString = mAlarmPref.mAlert.toString();
			final boolean vibrate = false;
			
			saveAlarm(this, mId, mAlarmOnPref.isChecked(), mHour, mMinutes,
					mDaysOfWeek, vibrate, label, alertString,
					popToast);
		}
	}

	/**
	 * Write alarm out to persistent store and pops toast if alarm enabled
	 */
	private static void saveAlarm(Context context, int id, boolean enabled,
			int hour, int minute, Alarms.DaysOfWeek daysOfWeek,
			boolean vibrate, String label, String alert, boolean popToast) {
		if (Log.LOGV) {
			Log.v("** saveAlarm " + id + " " + label + " " + enabled + " "
					+ hour + " " + minute + " vibe " + vibrate);
		}

		// Fix alert string first
		Alarms.setAlarm(context, id, enabled, hour, minute, daysOfWeek,
				vibrate, label, alert);

		if (enabled && popToast) {
			popAlarmSetToast(context, hour, minute, daysOfWeek);
		}
	}

	/**
	 * Display a toast that tells the user how long until the alarm goes off.
	 * This helps prevent "am/pm" mistakes.
	 */
	static void popAlarmSetToast(Context context, int hour, int minute,
			Alarms.DaysOfWeek daysOfWeek) {

		String toastText = formatToast(context, hour, minute, daysOfWeek);
		Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
		ToastMaster.setToast(toast);
		toast.show();
	}

	/**
	 * format "Alarm set for 2 days 7 hours and 53 minutes from now"
	 */
	static String formatToast(Context context, int hour, int minute,
			Alarms.DaysOfWeek daysOfWeek) {
		long alarm = Alarms.calculateAlarm(hour, minute, daysOfWeek)
				.getTimeInMillis();
		long delta = alarm - System.currentTimeMillis();
		;
		long hours = delta / (1000 * 60 * 60);
		long minutes = delta / (1000 * 60) % 60;
		long days = hours / 24;
		hours = hours % 24;

		String daySeq = (days == 0) ? "" : (days == 1) ? context
				.getString(R.string.day) : context.getString(R.string.days,
				Long.toString(days));

		String minSeq = (minutes == 0) ? "" : (minutes == 1) ? context
				.getString(R.string.minute) : context.getString(
				R.string.minutes, Long.toString(minutes));

		String hourSeq = (hours == 0) ? "" : (hours == 1) ? context
				.getString(R.string.hour) : context.getString(R.string.hours,
				Long.toString(hours));

		boolean dispDays = days > 0;
		boolean dispHour = hours > 0;
		boolean dispMinute = minutes > 0;

		int index = (dispDays ? 1 : 0) | (dispHour ? 2 : 0)
				| (dispMinute ? 4 : 0);

		String[] formats = context.getResources().getStringArray(
				R.array.alarm_set);
		return String.format(formats[index], daySeq, hourSeq, minSeq);
	}

	public boolean onCreateOptionsMenu(Menu menu) {
		return false;
		// super.onCreateOptionsMenu(menu);
		//
		// mDeleteAlarmItem = menu.add(0, 0, 0, R.string.delete_alarm);
		// mDeleteAlarmItem.setIcon(android.R.drawable.ic_menu_delete);
		//
		// if (ClockActivity.DEBUG) {
		// mTestAlarmItem = menu.add(0, 0, 0, "test alarm");
		// }
		//
		//
		// return true;
	}

	public boolean onOptionsItemSelected(MenuItem item) {
		// if (item == mDeleteAlarmItem) {
		// Alarms.deleteAlarm(this, mId);
		// finish();
		// return true;
		// }
		//
		// if (ClockActivity.DEBUG) {
		// if (item == mTestAlarmItem) {
		// setTestAlarm();
		// return true;
		// }
		// }

		return false;
	}

	/**
	 * Test code: this is disabled for production build. Sets this alarm to go
	 * off on the next minute
	 */
	void setTestAlarm() {
		// start with now
		java.util.Calendar c = java.util.Calendar.getInstance();
		c.setTimeInMillis(System.currentTimeMillis());

		int nowHour = c.get(java.util.Calendar.HOUR_OF_DAY);
		int nowMinute = c.get(java.util.Calendar.MINUTE);

		int minutes = (nowMinute + 1) % 60;
		int hour = nowHour + (nowMinute == 0 ? 1 : 0);

		String label = getTitle().toString(); // mLabel.getText()
		saveAlarm(this, mId, true, hour, minutes, mDaysOfWeek, true, label,
				mAlarmPref.mAlert.toString(), true);
	}

}
